package com.vr.cloudpicture.manager.upload;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.URLUtil;
import com.qcloud.cos.model.PutObjectResult;
import com.qcloud.cos.model.ciModel.persistence.CIObject;
import com.qcloud.cos.model.ciModel.persistence.ImageInfo;
import com.qcloud.cos.model.ciModel.persistence.ProcessResults;
import com.vr.cloudpicture.config.CosClientConfig;
import com.vr.cloudpicture.exception.BusinessException;
import com.vr.cloudpicture.exception.ErrorCode;
import com.vr.cloudpicture.manager.CosManager;
import com.vr.cloudpicture.model.dto.file.UploadPictureResult;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.List;

/**
 * 图片上传模板抽象类
 * 使用模板方法模式简化代码
 *
 * @author hzh
 * @date 2025/05/13
 */
@RequiredArgsConstructor
@Slf4j
public abstract class PictureUploadTemplate {
    protected static final long ONE_M = 1024 * 1024L;
    protected static final List<String> ALLOW_FORMAT_LIST = Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "webp");
    protected static final List<String> ALLOW_CONTENT_TYPE_LIST = Arrays.asList("image/jpeg", "image/jpg", "image/png", "image/webp");
    protected final CosManager cosManager;
    protected final CosClientConfig cosClientConfig;

    /**
     * 校验输入源
     *
     * @param inputSource 输入源
     */
    protected abstract void validPicture(Object inputSource);

    /**
     * 获取原始文件名
     *
     * @param inputSource 输入源
     * @return {@code String }
     */
    protected abstract String getOriginalFilename(Object inputSource);

    /**
     * 处理文件
     *
     * @param inputSource 输入源
     * @param uploadFile  上传文件
     * @throws IOException io异常
     */
    protected abstract void processFile(Object inputSource, File uploadFile) throws IOException;

    /**
     * 上传图片
     *
     * @param inputSource      上传的输入源，例如 MultipartFile, URL
     * @param uploadPathPrefix 上传路径前缀
     * @return {@code UploadPictureResult }
     */
    public UploadPictureResult uploadPicture(Object inputSource, String uploadPathPrefix)  {
        // 1.根据实际需求校验文件
        validPicture(inputSource);
        // 2. 获取原始文件名
        String originalFilename = getOriginalFilename(inputSource);
        // 3.生成图片上传地址
        String uploadFilename = String.format("%s_%s.%s", LocalDate.now(), RandomUtil.randomString(16), originalFilename);
        if (uploadFilename.contains("?")) {
            uploadFilename=uploadFilename.substring(0,uploadFilename.indexOf("?"));
        }
        String uploadPath = String.format("%s/%s", uploadPathPrefix, uploadFilename);
        // 处理ulr编码问题，当出现中文时，会出现乱码，会导致上传的key发生乱码，注意存储路径的时候要解码
        uploadPath = URLUtil.encode(uploadPath,StandardCharsets.UTF_8);
        // 4.生成临时文件
        File uploadFile = null;
        try {
            uploadFile = File.createTempFile(uploadPath, null);
            // 5.处理文件
            processFile(inputSource, uploadFile);
            // 6.上传
            PutObjectResult putObjectResult = cosManager.putPictureObject(uploadPath, uploadFile);
            // 8.封装返回结果
            ProcessResults processResults = putObjectResult.getCiUploadResult().getProcessResults();
            ImageInfo imageInfo = putObjectResult.getCiUploadResult().getOriginalInfo().getImageInfo();
            List<CIObject> objectList = processResults.getObjectList();
            if (CollUtil.isNotEmpty(objectList)) {
                CIObject compressCiObject = null;
                CIObject thumbnailCiObject=null;
                CIObject previewCiObject=null;
                // 可能设置规则的
                for (CIObject ciObject : objectList) {
                    String key = ciObject.getKey();
                    if (key.contains("thumbnail")) {
                        thumbnailCiObject=ciObject;
                    } else if (key.contains("preview")) {
                        previewCiObject=ciObject;
                    }else {
                        compressCiObject=ciObject;
                    }
                }
                // 缩略图和预览图没有的话，默认是压缩图
                if (objectList.size()<2) {
                    thumbnailCiObject=compressCiObject;
                }
                if (objectList.size()<3) {
                    previewCiObject=compressCiObject;
                }
                // 封装压缩图返回结果
                return buildResult(originalFilename,compressCiObject,thumbnailCiObject,previewCiObject,imageInfo);
            }
            // 封装原图返回结果
            return buildResult(originalFilename, uploadFile, uploadPath, imageInfo);
        } catch (IOException e) {
            log.error("file upload error {}", ExceptionUtil.getRootCauseMessage(e));
            throw new BusinessException(ErrorCode.OPERATION_ERROR,  "图片上传失败");
        } finally {
            // 删除临时文件
            deleteTempFile(uploadFile);
        }

    }


    protected void deleteTempFile(File uploadFile) {
        if (uploadFile != null) {
            boolean delete = uploadFile.delete();
            if (!delete) {
                log.error("file delete error,filepath={}", uploadFile.getAbsolutePath());
            }
        }
    }
    private UploadPictureResult buildResult(String originalFilename, File file,String uploadPath,ImageInfo imageInfo) {
        return UploadPictureResult.builder()
                .name((FileUtil.mainName(originalFilename)))
                .url(String.format("%s/%s", cosClientConfig.getHost(), uploadPath))
                .picFormat(imageInfo.getFormat())
                .picHeight(imageInfo.getHeight())
                .picWidth(imageInfo.getWidth())
                .picSize(file.length())
                // 缩放比例
                .picScale(NumberUtil.round(imageInfo.getWidth() * 1.0 / imageInfo.getHeight(), 2).doubleValue())
                .picColor(expendHexColor(imageInfo.getAve()))
                .build();
    }
    private UploadPictureResult buildResult(String originalFilename, CIObject compressCiObject, CIObject thumbnailCiObject, CIObject previewCiObject, ImageInfo imageInfo) {
        return UploadPictureResult.builder()
                .name(FileUtil.mainName(originalFilename))
                // 设置压缩后的图片地址,并解码
                .url(URLUtil.decode(String.format("%s/%s", cosClientConfig.getHost(), compressCiObject.getKey()),StandardCharsets.UTF_8))
                // 缩略图地址
                .thumbnailUrl(String.format("%s/%s", cosClientConfig.getHost(), thumbnailCiObject.getKey()))
                // 设置预览图地址
                .previewUlr(String.format("%s/%s", cosClientConfig.getHost(), previewCiObject.getKey()))
                .picFormat(compressCiObject.getFormat())
                .picHeight(compressCiObject.getHeight())
                .picWidth(compressCiObject.getWidth())
                .picSize(Long.valueOf(compressCiObject.getSize()))
                // 缩放比例
                .picScale(NumberUtil.round(compressCiObject.getWidth() * 1.0 / compressCiObject.getHeight(), 2).doubleValue())
                // 颜色值
                .picColor(expendHexColor(imageInfo.getAve()))
                .build();
    }
    protected String expendHexColor(String compressed) {
        // 去除可能存在的0x前缀
        String input = compressed.startsWith("0x") ? compressed.substring(2) : compressed;
        int length = input.length();
        // 长度为3直接返回
        if (length == 3) {
            return "0x000000";
        }
        int index = 0;
        StringBuilder expanded = new StringBuilder();
        // 处理三个颜色分量
        for (int i = 0; i < 3; i++) {
            char current = input.charAt(index);
            if (current == '0') {
                // 当前分量是00的情况
                expanded.append("00");
                index++;
            } else {
                // 正常分量处理（可能包含补零）
                if (index + 1 < length) {
                    expanded.append(current).append(input.charAt(index + 1));
                } else {
                    // 最后一个字符单独处理，补零
                    expanded.append(current).append('0');
                }
                index += 2;
            }
        }
        return "0x" + expanded.toString();
    }

}
